---
title: Plugin System
description: Complete guide to the plugin system in c15t Backend, including plugin types, lifecycle hooks, context extensions, and best practices.
lastModified: 2025-04-15
deprecated: true
deprecatedReason: "@c15t/backend v1 did not deliver the flexibility we wanted and fell short of our standards. It is now deprecated as we work on a full rewrite, with v2 entering canary soon. This does not affect consent.io deployments, which remain stable."
---

The c15t Backend plugin system provides a powerful way to extend and customize the functionality of your consent management system. This guide covers everything you need to know about plugins, from basic usage to advanced features.

## Overview

Plugins are modular components that can:

- Extend the context with additional data
- Modify requests and responses
- Add new routes and endpoints
- Provide custom middleware
- Hook into the request lifecycle

## Plugin Types

### Core Plugins

Core plugins are essential system components that provide fundamental functionality:

```typescript
const corePlugin: C15TPlugin = {
  id: 'core-plugin',
  name: 'Core Plugin',
  type: 'core',
  init: () => ({
    context: {
      systemInfo: {
        version: '1.0.0',
        environment: process.env.NODE_ENV,
      },
    },
  }),
};
```

### Feature Plugins

Feature plugins add specific functionality to your system:

```typescript
const featurePlugin: C15TPlugin = {
  id: 'feature-plugin',
  name: 'Feature Plugin',
  type: 'feature',
  init: () => ({
    routes: [
      {
        path: '/api/custom',
        method: 'GET',
        handler: async (request, ctx) => {
          return new Response(JSON.stringify({ message: 'Custom endpoint' }));
        },
      },
    ],
  }),
};
```

### Middleware Plugins

Middleware plugins process requests and responses:

```typescript
const middlewarePlugin: C15TPlugin = {
  id: 'middleware-plugin',
  name: 'Middleware Plugin',
  type: 'middleware',
  onRequest: async (request, ctx) => {
    // Add request processing
    return { request };
  },
  onResponse: async (response, ctx) => {
    // Add response processing
    return { response };
  },
};
```

## Plugin Lifecycle

### Initialization

Plugins are initialized when the c15t instance is created:

```typescript
const instance = c15tInstance({
  baseURL: 'http://localhost:3000',
  database: memoryAdapter({}),
  plugins: [
    corePlugin,
    featurePlugin,
    middlewarePlugin,
  ],
});
```

### Request Lifecycle

Plugins can hook into various stages of request processing:

```typescript
const lifecyclePlugin: C15TPlugin = {
  id: 'lifecycle-plugin',
  name: 'Lifecycle Plugin',
  type: 'middleware',
  onRequest: async (request, ctx) => {
    console.log('Request received');
    return { request };
  },
  onBeforeHandler: async (request, ctx) => {
    console.log('Before handler');
    return { request };
  },
  onAfterHandler: async (response, ctx) => {
    console.log('After handler');
    return { response };
  },
  onResponse: async (response, ctx) => {
    console.log('Response sent');
    return { response };
  },
};
```

## Context Extensions

Plugins can extend the context with additional data:

```typescript
const contextPlugin: C15TPlugin = {
  id: 'context-plugin',
  name: 'Context Plugin',
  type: 'core',
  init: () => ({
    context: {
      customData: {
        timestamp: Date.now(),
        requestId: generateId(),
      },
    },
  }),
};
```

## Custom Routes

Plugins can add custom routes to your API:

```typescript
const routePlugin: C15TPlugin = {
  id: 'route-plugin',
  name: 'Route Plugin',
  type: 'feature',
  init: () => ({
    routes: [
      {
        path: '/api/custom',
        method: 'GET',
        handler: async (request, ctx) => {
          const data = await ctx.database.find('custom', {});
          return new Response(JSON.stringify(data));
        },
      },
      {
        path: '/api/custom',
        method: 'POST',
        handler: async (request, ctx) => {
          const data = await request.json();
          const result = await ctx.database.create('custom', data);
          return new Response(JSON.stringify(result));
        },
      },
    ],
  }),
};
```

## Error Handling

Plugins can handle errors at different levels:

```typescript
const errorPlugin: C15TPlugin = {
  id: 'error-plugin',
  name: 'Error Plugin',
  type: 'middleware',
  onError: async (error, ctx) => {
    console.error('Plugin error:', error);
    return {
      response: new Response(
        JSON.stringify({ error: 'Internal server error' }),
        { status: 500 }
      ),
    };
  },
};
```

## Plugin Dependencies

Plugins can declare dependencies on other plugins:

```typescript
const dependentPlugin: C15TPlugin = {
  id: 'dependent-plugin',
  name: 'Dependent Plugin',
  type: 'feature',
  dependencies: ['core-plugin', 'auth-plugin'],
  init: (ctx) => {
    // Access dependent plugin data
    const coreData = ctx.plugins['core-plugin'].data;
    const authData = ctx.plugins['auth-plugin'].data;
    
    return {
      context: {
        combinedData: {
          ...coreData,
          ...authData,
        },
      },
    };
  },
};
```

## Best Practices

1. **Plugin Organization**
   ```typescript
   // plugins/index.ts
   export const plugins: C15TPlugin[] = [
     corePlugin,
     authPlugin,
     loggingPlugin,
     customPlugin,
   ];
   ```

2. **Error Handling**
   ```typescript
   const safePlugin: C15TPlugin = {
     id: 'safe-plugin',
     name: 'Safe Plugin',
     type: 'middleware',
     onRequest: async (request, ctx) => {
       try {
         // Plugin logic
         return { request };
       } catch (error) {
         console.error('Plugin error:', error);
         return { error };
       }
     },
   };
   ```

3. **Performance Optimization**
   ```typescript
   const optimizedPlugin: C15TPlugin = {
     id: 'optimized-plugin',
     name: 'Optimized Plugin',
     type: 'middleware',
     onRequest: async (request, ctx) => {
       // Cache expensive operations
       const cacheKey = request.url;
       const cached = await ctx.cache.get(cacheKey);
       if (cached) {
         return { request, cached };
       }
       
       // Perform operation
       const result = await expensiveOperation();
       await ctx.cache.set(cacheKey, result);
       
       return { request, result };
     },
   };
   ```

## Testing Plugins

### Unit Testing

```typescript
describe('Custom Plugin', () => {
  it('should extend context', async () => {
    const plugin = customPlugin;
    const ctx = { context: {} };
    
    const result = await plugin.init(ctx);
    expect(result.context).toHaveProperty('customData');
  });
  
  it('should handle requests', async () => {
    const plugin = customPlugin;
    const request = new Request('http://localhost:3000');
    const ctx = { context: {} };
    
    const result = await plugin.onRequest(request, ctx);
    expect(result.request).toBeDefined();
  });
});
```

### Integration Testing

```typescript
describe('Plugin Integration', () => {
  it('should work with other plugins', async () => {
    const instance = c15tInstance({
      baseURL: 'http://localhost:3000',
      database: memoryAdapter({}),
      plugins: [plugin1, plugin2],
    });
    
    const request = new Request('http://localhost:3000/api/test');
    const response = await instance.handler(request);
    
    expect(response.status).toBe(200);
  });
});
```

## Common Issues

1. **Plugin Order**
   - Plugins are executed in the order they are provided
   - Dependencies should be listed first
   - Core plugins should be initialized before feature plugins

2. **Context Access**
   - Always check for context existence
   - Use optional chaining for nested properties
   - Provide default values when needed

3. **Error Propagation**
   - Handle errors at the appropriate level
   - Log errors for debugging
   - Return appropriate error responses
